import { spawn } from 'child_process';
import { existsSync, promises as fs, mkdirSync } from 'fs';
import { readJSON, readJSONSync, writeJsonSync } from 'fs-extra';
import path from 'path';
import { runCmd } from './Utils';
import { VersionConfig } from './types';

type DiffStatus = 'A' | 'M' | 'D';
interface DiffItem {
    status: DiffStatus;
    filePath: string;
}


type VersionPackOptions = {
    commit1: string;
    commit2: string;
    targetDir: string;
    outputDir: string;
    version: number;
    root: string;
    verJsonDir: string;
    versionData: Record<string, number>
}

const PROJECT_ROOT = path.resolve(__dirname, '../..');

function getFirstCommitHash(repoPath: string): string {
    return runCmd('git rev-list --max-parents=0 HEAD', repoPath).trim();
}

function removeFirstDirSafe(pathStr: string): string {
    const normalizedPath = pathStr.replace(/\\/g, '/');
    const pathData = path.parse(normalizedPath);
    const segments = pathData.dir.split('/').filter(s => s.trim() !== '');

    if (segments.length === 0 || (path.isAbsolute(pathStr) && segments.length === 1)) {
        return pathData.base;
    }
    if (segments.length === 1)
        return pathData.base;
    return `${segments.slice(1).join('/')}/${pathData.base}`;
}

async function processDiff(commit1: string, commit2: string, targetDir: string, cwd: string): Promise<DiffItem[]> {
    const isFirstCommit = commit1 === runCmd('git rev-list --max-parents=0 HEAD', cwd).trim();
    let diffOutput = '';

    if (isFirstCommit) {
        const initialFiles = runCmd(`git ls-tree -r --name-only ${commit2} -- ${targetDir}`, cwd).trim();
        diffOutput = initialFiles.split('\n').map(file => `A\t${file}`).join('\n');
    } else {
        diffOutput += runCmd(`git diff --name-status --no-renames ${commit1} ${commit2} -- ${targetDir}`, cwd).trim();
    }

    return diffOutput.split('\n')
        .filter(line => line.trim())
        .map(line => {
            const [status, filePath] = line.split('\t');
            return { status: status[0] as DiffStatus, filePath };
        });
}

async function generateVersionFiles({
    commit1,
    commit2,
    targetDir,
    outputDir,
    version,
    root,
    verJsonDir,
    versionData = {}
}: VersionPackOptions): Promise<boolean> {
    const diffList = await processDiff(commit1, commit2, targetDir, root);
    if (!diffList.length) {
        console.warn("没有更新内容")
        return false
    }
    await fs.mkdir(outputDir, { recursive: true });
    let uploadFiles = []
    for (const { status, filePath } of diffList) {
        if (status === 'D') continue;

        const destPath = path.join(outputDir, status === 'A' ? '0' : `${version}`);
        try {
            const fullPath = path.join(destPath, removeFirstDirSafe(filePath));
            const dirPath = path.dirname(fullPath);

            if (!existsSync(dirPath)) {
                mkdirSync(dirPath, { recursive: true });
            }
            spawn('git', ['show', `${commit2}:${filePath}`, '>', fullPath], {
                shell: true,
                cwd: targetDir,
                stdio: 'inherit'
            });
            uploadFiles.push(fullPath);
            if (status !== 'A')
                versionData[removeFirstDirSafe(filePath)] = version;
        } catch (error) {
            console.error(`文件处理失败: ${filePath}`, error);
        }
    }
    uploadFiles.push(path.join(verJsonDir, `${version}.json`))
    writeJsonSync(path.join(verJsonDir, `${version}.json`), versionData, { encoding: 'utf-8', spaces: 4 })
    writeJsonSync(path.join(root, `upload.json`), uploadFiles, { encoding: 'utf-8', spaces: 4 })
    return true
}

async function updateConfig(configJson: VersionConfig, commit: string, output_dir: string, root: string) {
    let oldLastCommit = configJson.lastCommit || "0"
    if (commit == "HEAD") {
        configJson.lastCommit = runCmd("git rev-parse --short HEAD", root).trim()
    }
    else {
        configJson.lastCommit = commit
    }

    configJson.verRecord[configJson.ver] = [oldLastCommit, configJson.lastCommit]
    configJson.ver++
    writeJsonSync(output_dir, configJson, { encoding: 'utf-8', spaces: 4 })
    console.log(`更新配置完毕: ${configJson.ver - 1}, ${configJson.lastCommit}`);
}

export async function versionPack(root: string, nextCommit = "HEAD") {
    const configPath = path.resolve(path.join(root, 'config.json'));

    let config: VersionConfig
    try {
        config = readJSONSync(configPath)
    } catch {
        config = { ver: 0, lastCommit: '', verRecord: {} };
    }

    const version = config.ver;
    const targetDir = path.resolve(path.join(root, 'game'));
    const outputDir = path.resolve(path.join(root, 'upRes', "res"));
    const verJsonPath = path.resolve(path.join(root, 'ver', `${version - 1}.json`));
    const verJsonDir = path.resolve(path.join(root, "ver"));

    let versionData: Record<string, number> = {};
    if (version > 1) {
        try {
            versionData = await readJSON(verJsonPath)
        } catch (error) {
            console.log(error);
        }
    }

    const firstCommit = getFirstCommitHash(targetDir);
    let b = await generateVersionFiles({
        commit1: config.lastCommit || firstCommit,
        commit2: nextCommit,
        targetDir,
        outputDir,
        version,
        verJsonDir,
        root,
        versionData
    });

    if (b)
        updateConfig(config, nextCommit, configPath, root)
}

// const root = "C:/Users/Administrator/Desktop/cocos/ver";

// versionPack(root);
